Still, the Hack API is intended to be an evolving structure. If there is some feature that you would like your extension to be able to rely upon, feel free to email me at mistered@1stresource.com and I'll see if I can roll it into the next HackMaster revision.
Introduction
The Hack File Format
Compiling and Linking a
Hack
Trap
Patch Interaction Details
Control Panel Interaction
Details
The Custom
Procedure
Licensing
Information
Conclusion
For instance, one could code up a Hack to replace the standard Keyboard
dialog with a new input method, or insert a procedure into the event-handling
code in order to perform special actions at the entry of a certain Graffiti
character, or even patch the text field routines to allow hyperlinking between
applications. The potential uses are even wider than the system software itself.
Unfortunately, there are some problems with implementation. Where in memory
does one put the routine? How does one install it into the trap? How do you deal
with multiple patches on the same trap, especially if they install and remove
themselves in different orders? How do you gracefully recover from a system
reset?
It is to solve these problems that HackMaster was written. HackMaster reads
in special PalmPilot resource files of type 'HACK', which contain the code for
the patch and also whatever user interface routines are necessary. Then
HackMaster presents a standard interface to the user for installing and removing
Hacks, and for changing their configurations. It handles all the dirty work of
installing and removing the trap patches and keeping track of multiple Hacks on
the same trap. And it saves the current extension configuration so that they can
all be automatically (or optionally) reinstalled on a system reset.
This document, then, will take you through the process of writing a PalmOS
system extension using the HackMaster protocol. In doing so, you will be
bypassing a lot of effort in the details of trap patching, and you will be
ensuring that your extensions will automatically coexist peacefully with all
other possible extensions.
I assume a fairly high level of Pilot programming knowledge. You should
definitely be able to code up a standalone PalmOS application before attempting
to write a Hack, since the facilities for debugging are much less and the
potential for mishaps is much greater. Some of the techniques employed here are
not part of the standard PalmOS documentation, but with any luck Palm may adopt
HackMaster for 'official' use in the future. (If you want to induce them to,
send a little note to devsupp@palm.com...)
For more information on compiling and linking the necessary 'code' resources,
skip to the next
section. This section outlines the larger structure of the .prc file,
specifying the resource IDs and types for the various parts of the Hack to be
recognized and function properly.
The Hack file type is 'HACK', instead of that standard 'appl' for application
files. Note that each Hack, just like each application, requires a unique
creator code to identify it. These creator codes should be registered with Palm
Computing just like applications to prevent conflicts. Anyway, here are the
resources to include:
The 'code' resources will be called directly by the trap dispatcher, so be
sure they are linked in the appropriate way (see the next section). It is
possible to have multiple patches in the same file patching the same trap, but
there isn't much purpose to this anyway. Almost any trap can be patched, with
the major exception of the Feature Manager calls.
In general, unless you have a very good reason, you should almost always
choose to supplement the HackMaster behavior rather than replace it. For more
details on this custom routine, see a later section of this
file.
Introduction
HackMaster is a program for managing extensions to the Palm
OS system software, affectionately known as "Hacks". Typically, such extensions
hook themselves into one of the many system routine traps and thus are called in
addition to or in lieu of the standard Palm OS routines.
The 'HACK' .PRC File
The files read by HackMaster are standard Palm
resource files, just like standalone applications. They contain code resources
and UI resources and whatever else you want to stuff into the resource database.
They are installed just like applications, but won't show up as programs to be
run, though they can be deleted in the Memory utility in the normal way.
The patch routines
The actual routines that will be inserted into the
system as a trap patch should be placed in 'code' resources with IDs starting
with 1000 and increasing sequentially. Each Hack file can contain several
patches that will all be installed and removed at the same time, if a single
desired function requires multiple patches.
The trap codes
Along with each patch 'code' resource, a resource of type
'TRAP' must exist with the same ID containing the 2-byte trap code that the
patch should be installed in. To determine the trap code, have a look at the
SysTraps.h PalmOS header file, which declares a large enumerated type for them.
If you don't want to count them yourself, here is an annotated copy of
the header file with the trap codes listed directly.
Custom routine
An optional 'code' resource of ID 1500 can contain a
routine which handles custom extension behavior. It can override or supplement
the standard HackMaster installation, removal, and reset recovery behavior. You
can use this to install patches in places besides traps, such as interrupt
service routines or application patches. This can also be used to initialize
database structures for the extension, and so forth, or clean up allocated
memory after a reset.
The Hack name
A 'tAIN' resource of ID 3000 contains the name that is
listed in the HackMaster directory. If this is not present, your Hack will still
be listed, but with the label 'unknown'.
The Hack icon
An optional 'ICON' resource of ID 3000 contains an icon
for your Hack. This resource is not presently supported by HackMaster, but I
felt I should include this future possibility in the API from the start, just in
case.
The about box
An optional 'tFRM' resource of ID 3000 contains a dialog
box that will be displayed when the user presses the question mark icon in the
HackMaster list. This form will just be called with FrmDoDialog, so no user
interaction is supported beyond pressing an OK button. It should describe the
function of the extension, and provide whatever author information you like.
The control panel forms
An optional 'tFRM' resource of ID 2000 contains
a form which will be displayed when the user presses the plus icon in the
HackMaster list. This form should be used to support any options that the
extension needs to have set. Some patches require no interaction, but others
need a lot of configuration. This form is allowed to call other forms in the ID
range 2000-2999 if necessary, but the ID 2000 form is the only one launched
directly by HackMaster. For more details on the interaction between the control
panel forms and HackMaster, see a later section of this
document.
The basic idea is that you want to link your code in such a way that
HackMaster can call the procedure you want by just jumping to the beginning
address of your code resource. To do this, you will need a separate C source
file for each code resource, and a separate compile and link statement for each
one. Only in the building of the resource file do all the parts come together.
Unfortunately, since you are not compiling a free-standing application, but
just a code fragment, you will not be able to use the Pilot Simulator at all.
All of the compiling is done in MPW, and all debugging will be on-Pilot. In the
future, we may be able to figure out a way to write a trap patch shell to run on
the simulator, but for now you're living on the bleeding edge.
Quite simple, actually. For a control panel event handler, you do the exact
same thing, except that the routine to be called should have the functional form
of a standard event handler routine (the same as above, but just because we
coincidentally picked an event-handler routine to trap).
In the above sample, the directories and compiler options should already be
defined by the makefile in the normal way. This step will get you the object
files you need to link into the code resources.
The first thing different is that the
Note that in this resource file, no mention is made of the CODE 0 and DATA 0
resources, which contain global variable information in a full Pilot program.
Here, just the one CODE resource is compiled as a 'code' resource of ID 1000,
exactly as a trap patch must be. If you had a control panel event handler, you
would need a second include statement:
The last step is to give MPW the command to build the .PRC file:
Note that the application type for a Hack file is 'HACK', intead of 'appl'.
Other than that, you get a standard .prc file that can be installed and deleted
in the usual way. However, it will not show up in the Applications dialog, but
only in the HackMaster list.
Here is a sample code snippet showing how your trap routine on SysHandleEvent
in ID 1000 should call the system routine: Depending on the function of the Hack, the original routine can be used in
different ways. It could be ignored entirely, in order to replace its
functionality entirely with that of the Hack. Actions could be taken by the
Hack, and then the original routine called, as for instance an event handler
patch. The parameters of the routine could be altered and then the original
called, or the original called and then its results manipulated. In general,
unless you have a reason not to, the original system routine should be called at
some point, if for no other reason than to allow other Hacks installed on the
same trap to process the event too.
Hacks can be installed and removed at will, so be sure to get this address
new at every function call, since it may have been changed to reflect a new trap
patch.
Upon installation, the patch 'code' resource is locked in memory, and remains
so for the duration of its use. It is in one of the static heaps, though, so
memory protection needs to be kept in mind if you have some visions of
self-modifying code.
HackMaster will trap frmLoadEvents with IDs in the range 2000-2999 and
perform the necessary setup for them. It will open the Hack's resource file and
call FrmInitForm for the form in question. It will also install the 'code'
resource with the same ID as the event handler for that form using
FrmSetEventHandler.
HackMaster will also trap frmCloseEvents. Upon doing so, it will immediately
pass the event on to the Hack's event handler and/or the standard form event
handler using FrmDispatchEvent. After this call returns, HackMaster will close
the Hack's resource file again.
If other forms need to be brought up by the control panel, this can be done
with FrmGotoForm commands. FrmPopupForm is not recommended because this will
probably result in the resource file being opened twice by HackMaster.
The control panel must provide a means for the user to exit it, such as a
'Done' button. At such a close, the control panel should execute a
FrmGotoForm(9000) command to jump back to the HackMaster main list.
The control panel event handler, just like any other 'code' resource
discussed above, cannot rely on global variables. Feature manager calls possibly
in combination with database or dynamic heap allocation should be used to save
whatever state information is necessary between event handler calls.
Note that since the control panel handler is called by the form event
dispatcher, system events will already have been handled at an earlier stage, so
it is not currently possible for the control panel to respond to system-level
events, such as hardware app button presses, for instance, without serious
workarounds.
The control panel must be able to gracefully handle the case of being brought
up when the extension is installed or not. If configuring of the extension is
not possible while it is running, the control panel should detect this situation
(with Feature Manager calls) and display a "sorry, can't do that" dialog.
Anyway, if you do want to distribute copies of HackMaster with other software
you have written, we'll have to arrange some sort of license. Specifics vary on
a case-by-case basis, but generally I would expect a licensing fee per copy of
25 cents or one percent of the cost of the product, whichever is less. This
entitles you to distribute copies of HackMaster, but does not mean that your
customers are automatically registered users with all the rights and privileges
thereof (like technical support, email updates, etc.)
Anyway, just email me at mistered@1stresource.com and we'll
talk specifics if this applies to you. If, on the other hand, you are a
shareware/freeware author and want to distribute copies of HackMaster with your
Hacks, go right ahead. Just be somewhat obvious in mentioning the fact that
HackMaster is indeed shareware are should be registered with DaggerWare; the
appropriate URL is in the program itself.
Compiling and Linking a Hack
In several places, you are required to
supply a procedure to be executed in a separate 'code' resource. This is a minor
exercise in creative compiling and linking, but once you set it up right, the
process is completely automated in MPW.
The source code
At a minimum, you will need a source code file for your
trap patch. You may need some more separate files for additional patches and for
control panel event handlers. In any case, the source code does not contain a
PilotMain routine or even a C main routine. Just write the procedure you want to
be called in the code resource (the routine will be specified in the link
stage). For example, a trap patch source file that will patch SysHandleEvent
looks like this:
#include <Pilot.h>
//no global variables allowed!
/*
Put procedures called by the main routine here.
*/
Boolean MySysHandleEventTrap(EventPtr event)
{
// the trap patch routine here
}
Compiling
In MPW, the compile step is exactly the same as for a standard
Pilot application. You should include a compile statement for each source code
file in your trap patch project:
"{OBJ_DIR}mypatch1.c.o" Ä MakeFile "{SRC_DIR}mypatch1.c"
{CPP} -o "{OBJ_DIR}mypatch1.c.o" "{SRC_DIR}mypatch1.c" ¶
{C_OPTIONS}
Linking
This is the different step, in which you tell MPW that you're
not making a Pilot application, but freestanding code fragments instead. The
link options are different for just this case, and you do not include the
standard startup code object file like you do for regular Pilot projects. Here
is a sample link statement:
LINK_OPTIONS = -single -coderesource -rt CODE=1
{LINK} {LINK_OPTIONS} -t rsrc -c RSED -m MyRoutine ¶
"{OBJ_DIR}mypatch1.c.o" -o mypatch1.code
-custom link type has been
replaced with a -coderesource tag, indicating that the code should
be linked into a 'CODE' resource of ID 1 in the Macintosh resource output file.
Secondly, the -m tag is used in the link statement in order to
specify what routine in the file should be put in the beginning of the resource.
This is done in such a way that this routine will be run by just jumping to the
first byte of the CODE resource, which is how HackMaster knows where your
routines are.
Resource compiling
Now we need to tell MPW how to put all these CODE
resources together with the patch's UI resources into a single .PRC file. For
this, the important part is the .r resource definition file. Here is a sample:
#include <BuildRules.h>
#include <SystemMgr.rh>
include "mypatch1.code" 'CODE' 1 as sysResTAppCode 1000;
#if LANGUAGE==LANGUAGE_ENGLISH
include ":Rsc:mypatch.rsrc";
#else
#error "The compiler variable LANGUAGE must be defined"
#endif
include "mypatch2.code" 'CODE' 1 as sysResTAppCode 2000; {CC} -d RESOURCE_COMPILER {C_OPTIONS} ¶
-e "{SRC_DIR}mypatch.r" > mypatch.i
PilotRez -v 1 -t HACK -c ???? -it mypatch.i -ot "My Patch"
Duplicate -y "MyPatch" "mypatch.prc"
Trap Patch Interaction Details
For an extension to successfully interact
with the system and with other extensions, a little bit of care is necessary.
The duties of a Hack and restrictions on the actions a Hack can perform are
severe, due to the fact that traps can be called from many different states of
the Pilot.
Calling the original trap routine
Upon installation, HackMaster creates
a feature keyed by the patch's creator code and resource ID which contains the
address of the original trap routine. The patch should obtain this address from
the Feature Manager and use it appropriately. If multiple patches are installed
on the same trap, this address might actually be that of the next patch in line
instead of the actual system routine.
#define mycreator '????'
#define myresourceID 1000
Boolean MySysHandleEventTrap (EventPtr event)
{
Boolean (*oldtrap)(EventPtr); //procedure pointer to old trap;
DWord temp; //for the feature manager call type checking
Boolean handled;
FtrGet(mycreator,myresourceID,&temp); //get old trap address from HackMaster
oldtrap=(Boolean (*)(EventPtr))temp; //set procedure pointer
handled=false;
//do whatever you want to with the event, handle it or not
if (!handled) { //if you didn't handle it, call the old routine
handled=oldtrap(event);
}
return handled;
}
Data storage issues
Patches are free-floating code snippets, so they do
not have global variable data. They inherit the stack of the currently running
procedure, so should not use excessive amounts of stack space. The dynamic heap
may be filled or empty, depending on whether or not the running program is using
it extensively (only use this for temporary storage, since dynamic heap space is
extremely precious). Patches may access databases and resource files and
features in order to retrieve configuration data stored for them by the control
panel routines.
Error handling
A Hack is in a unique situation. Since it is installed on
an existing system routine, it doesn't have to do anything. A
default behavior is already present in the system routine. Therefore Hacks have
no excuse to crash. Error check frequently, and if anything goes wrong, just do
nothing but call the original system routine and let it deal with the situation.
Control Panel Interaction Details
The interaction between HackMaster and
the control panel form resources contained in Hack files is very specific.
Failure to follow the details may result in fatal errors, so make sure you are
doing what HackMaster expects you to do.
Duties of HackMaster
At the press of the plus icon, HackMaster executes
a FrmGotoForm(2000) command, jumping to the form with ID 2000, if present. This
is the only form directly launched by HackMaster in the control panel interface,
although the ID 2000 form may jump to other forms in the ID range 2000-2999
without difficulty.
Duties of the control panel
The control panel must handle frmOpenEvents
in order to draw the form and initialize any structures it needs. It can rely
upon its resource file being opened and accessible for reading in other
resources.
The Custom Procedure
The API for the custom install/remove procedure in
ID 1500 is still being developed. Further details will be forthcoming when
available.
Licensing Information
Ah, finance. If you are a commercial company and
you would like to write and sell your own Hacks using this protocol, you are
free and clear to do so, and in fact I encourage you to adopt this standard to
prevent extension conflicts. However, the HackMaster program itself is
copyrighted shareware, and thus cannot be freely distributed
with the extensions you sell. It's not fair for you to get the profit for giving
customers my program, now is it?